/*
 * Copyright 2008-2023 dexian.vip. All rights reserved.
 * Support: http://www.dexian.vip
 * License: http://www.dexian.vip/license
 */

package vip.dexian.admin.service.impl;

import vip.dexian.admin.assembler.DepartmentCommandAssembler;
import vip.dexian.admin.assembler.DepartmentDTOAssembler;
import vip.dexian.admin.assembler.DepartmentMappingTargetAssembler;
import vip.dexian.admin.command.DepartmentSaveCommand;
import vip.dexian.admin.command.DepartmentUpdateCommand;
import vip.dexian.admin.dao.DepartmentDao;
import vip.dexian.admin.dto.DepartmentDTO;
import vip.dexian.admin.entity.Department;
import vip.dexian.admin.service.DepartmentService;
import vip.dexian.common.CommonAttributes;
import vip.dexian.common.utils.NumberUtils;
import vip.dexian.core.base.service.BaseServiceImpl;
import vip.dexian.core.base.validation.group.Save;
import vip.dexian.core.base.validation.group.Update;
import vip.dexian.core.config.redis.RedisCacheKeyAttributes;
import vip.dexian.core.exception.ServiceValidationException;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

/**
 * 部门
 *
 * @author 挺好的 2023年05月31日 10:13
 */
@Service ("departmentServiceImpl")
@Slf4j
public class DepartmentServiceImpl extends BaseServiceImpl <Department, DepartmentDTO, Long>
        implements DepartmentService {

    /**
     * 部门Dao
     */
    @Resource (name = "departmentDaoImpl")
    private DepartmentDao departmentDao;

    /**
     * Entity -> DTO
     */
    @Resource (name = "departmentDTOAssemblerImpl")
    private DepartmentDTOAssembler departmentDTOAssembler;

    /**
     * Command -> Entity
     */
    @Resource (name = "departmentCommandAssemblerImpl")
    private DepartmentCommandAssembler departmentCommandAssembler;

    /**
     * 部门属性复制
     */
    @Resource (name = "departmentMappingTargetAssemblerImpl")
    private DepartmentMappingTargetAssembler departmentMappingTargetAssembler;

    /**
     * 查找部门分类树
     *
     * @return 部门分类树
     */
    @Override
    @Transactional (propagation = Propagation.SUPPORTS, readOnly = true)
    @Cacheable (value = RedisCacheKeyAttributes.DEPARTMENT_TREE_KEY, unless = "#result == null")
    public List <DepartmentDTO> findTree () {
        List <Department> departments = this.departmentDao.findAll();
        return this.departmentDTOAssembler.toDTO(departments);
    }

    /**
     * 保存部门信息
     *
     * @param command
     *         保存指令
     *
     * @return {@link vip.dexian.admin.entity.Department}
     */
    @Override
    @Transactional (rollbackFor = Exception.class)
    @CacheEvict (value = RedisCacheKeyAttributes.DEPARTMENT_TREE_KEY, allEntries = true)
    public Department save (DepartmentSaveCommand command) {
        log.info("save command:{}", command);
        Department department = this.departmentCommandAssembler.toSave(command, Save.class);

        // 设置上级部门信息
        this.setParent(department, command.getParentId());
        // 设置值
        this.setValue(department);

        return super.save(department);
    }

    /**
     * 修改部门信息
     *
     * @param command
     *         修改指令
     *
     * @return {@link vip.dexian.admin.entity.Department}
     */
    @Override
    @Transactional (rollbackFor = Exception.class)
    @CacheEvict (value = RedisCacheKeyAttributes.DEPARTMENT_TREE_KEY, allEntries = true)
    public Department update (DepartmentUpdateCommand command) {

        log.info("update command:{}", command);

        Department department = this.departmentCommandAssembler.toUpdate(command, Update.class);

        // 数据库中的部门信息
        Department dbDepartment = this.departmentDao.findByValidationIfNull(command.getId(), "部门");

        // 设置上级部门
        this.setParent(dbDepartment, command.getParentId());
        // 设置属性结构数据
        this.setValue(dbDepartment);
        // 复制其他属性
        this.departmentMappingTargetAssembler.copy(department, dbDepartment);

        List <Department> children = this.departmentDao.findChildren(command.getId());

        for (Department child : children) {
            this.setValue(child);
        }

        return dbDepartment;
    }

    /**
     * 删除部门
     *
     * @param id
     *         实体类ID
     */
    @Override
    @Transactional (rollbackFor = Exception.class)
    @CacheEvict (value = RedisCacheKeyAttributes.DEPARTMENT_TREE_KEY, allEntries = true)
    public void delete (Long id) {
        log.info("delete parameter:{}", id);

        Department department = this.departmentDao.findByValidationIfNull(id, "部门");

        // 是否存在下级部门
        if (this.departmentDao.hasChildren(id)) {
            throw new ServiceValidationException("A10003");
        }

        super.delete(department);
    }

    /**
     * 设置上级部门
     *
     * @param department
     *         客户端提交的信息
     * @param parentId
     *         上级部门ID
     */
    private void setParent (Department department, Long parentId) {

        // 上级不能是自己
        if (NumberUtils.equals(parentId, department.getId())) {
            throw new ServiceValidationException("A10001");
        }

        if (NumberUtils.isPositive(parentId)) {

            Department parent = this.departmentDao.find(parentId);

            if (parent == null) {
                throw new ServiceValidationException("00003", "上级部门");
            }

            // 选中的上级 的parentId
            Long[] parentIds = parent.getParentIds();

            // 如果上级部门包含了 当前部门的id，则不可修改
            if (ArrayUtils.contains(parentIds, department.getId())) {
                throw new ServiceValidationException("A10002");
            }

            department.setParent(parent);

        } else {
            department.setParent(null);
        }
    }

    /**
     * 设置路径信息
     *
     * @param department
     *         当前部门
     */
    private void setValue (Department department) {

        if (department == null) {
            return;
        }

        Department parent = department.getParent();

        if (parent != null) {
            department.setTreePath(parent.getTreePath() + parent.getId() + CommonAttributes.SEPARATOR);
        } else {
            department.setTreePath(CommonAttributes.SEPARATOR);
        }

        department.setGrade(department.getParentIds().length);
    }
}
